﻿using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Drawing;
using System.Diagnostics;

namespace Win32API
{
    public partial class WinApi
    {       
        /// <summary>
        /// 判断函数调用时指定虚拟键的状态,确定用户当前是否按下了键盘上的一个键的函数。如果按下,则返回值。
        /// </summary>
        /// <param name="nVirtKey"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        static extern short GetAsyncKeyState(int nVirtKey);

        /// <summary>
        /// 判断 alt\shift\control 是否有按键被按下
        /// </summary>
        /// <returns></returns>
        public static bool CONTROL_ALT_ShiftIsDown()
        {
            return GetAsyncKeyState((int)VK.VK_SHIFT) < 0 || GetAsyncKeyState((int)VK.VK_ALT) < 0 || GetAsyncKeyState((int)VK.VK_CONTROL) < 0;
        }

        /// <summary>
        /// 创建窗口
        /// </summary>
        [DllImport("user32.dll", EntryPoint = "CreateWindowEx", SetLastError = true)]
        public static extern IntPtr CreateWindowEx(
            int dwExStyle, string lpClassName, string lpWindowName, int dwStyle,
            int X, int Y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu,
            IntPtr hInstance, int lpParam);

        /// <summary>
        /// 方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递
        /// </summary>
        /// <param name="hhwnd"></param>
        /// <param name="msg"></param>
        /// <param name="wparam"></param>
        /// <param name="lparam"></param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        private static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam, IntPtr lparam);

        /// <summary>
        /// 获取一个前台窗口的句柄（用户当前工作的窗口）
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        public static extern IntPtr GetForegroundWindow();

        /// <summary>
        /// 该函数可以获得与调用线程的消息队列相关的活动窗口的窗口句柄
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true)]
        public static extern IntPtr GetActiveWindow();

        /// <summary>
        /// 根据坐标获取窗口句柄
        /// </summary>
        /// <param name="Point"></param>
        /// <returns></returns>
        [DllImport("user32")]
        public static extern IntPtr WindowFromPoint(Point Point);

        /// <summary>
        /// 销毁指定的窗口
        /// </summary>
        /// <param name="handle"></param>
        /// <returns></returns>
        [DllImport("user32")]
        public static extern bool DestroyWindow(IntPtr handle);
        

        /// <summary>
        /// 函数获得一个指定子窗口的父窗口句柄
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "GetParent")]
        public static extern IntPtr GetParent(IntPtr hWnd);

        public static string GetForeWindow2()
        {
            var title = new StringBuilder(256);
            //焦点窗口句柄
            var ip = GetForegroundWindow();
            if (ip != IntPtr.Zero)
            {
                //获取窗体标题
                GetWindowText(ip, title, title.Capacity);
            }
            return title.ToString();
        }

        // https://baike.baidu.com/item/SetWindowPos/6376849?fr=aladdin
        /// <summary>
        /// 排序窗口
        /// 改变一个(子窗口,弹出式窗口,顶层窗口)的尺寸,位置,Z序.
        /// 根据它们在屏幕上出现的顺序排序,顶层窗口设置的级别最高,
        /// 并且被设置为Z序的第一个窗口. 
        /// </summary>
        /// <param name="hWnd">在z序中的位于被置位的窗口前的窗口句柄</param>
        /// <param name="hWndInsertAfter">用于标识在z-顺序的此 CWnd 对象之前的 CWnd 对象。
        /// 如果uFlags参数中设置了SWP_NOZORDER标记则本参数将被忽略。可为下列值之一：</param>
        /// <param name="X">以客户坐标指定窗口新位置的左边界。</param>
        /// <param name="Y">以客户坐标指定窗口新位置的顶边界。</param>
        /// <param name="cx">以像素指定窗口的新的宽度。</param>
        /// <param name="cy">以像素指定窗口的新的高度。</param>
        /// <param name="uFlags">窗口尺寸和定位的标志。该参数可以是下列值的组合：</param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "SetWindowPos")]
        public static extern bool SetWindowPos(
        IntPtr hWnd,
        IntPtr hWndInsertAfter,
        int X,
        int Y,
        int cx,
        int cy,
        uint uFlags
        );

        public static bool SetWindowPos(IntPtr Handle)
        {
            // 显示状态
            uint swp = SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED; //0x23 
            return SetWindowPos(Handle, IntPtr.Zero, 0, 0, 0, 0, swp);//会触发刷新
        }

     


        //SetWindowPos用的
        public static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        public static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        public static readonly IntPtr HWND_TOP = new IntPtr(0);
        public const uint SWP_NOSIZE = 0x0001;
        public const uint SWP_NOMOVE = 0x0002;
        public const uint SWP_NOZORDER = 0x0004;
        public const uint SWP_NOREDRAW = 0x0008;
        public const uint SWP_NOACTIVATE = 0x0010;
        public const uint SWP_FRAMECHANGED = 0x0020;
        public const uint SWP_SHOWWINDOW = 0x0040;
        public const uint SWP_HIDEWINDOW = 0x0080;
        public const uint SWP_NOCOPYBITS = 0x0100;
        public const uint SWP_NOOWNERZORDER = 0x0200;
        public const uint SWP_NOSENDCHANGING = 0x0400;
        public const uint TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;



        /// https://www.cnblogs.com/zhuiyi/archive/2012/07/09/2583024.html
        /// <summary>
        /// 设置目标窗体大小,位置(屏幕区坐标)
        /// </summary>
        /// <param name="hWnd">目标句柄</param>
        /// <param name="x">目标窗体新位置X轴坐标</param>
        /// <param name="y">目标窗体新位置Y轴坐标</param>
        /// <param name="nWidth">目标窗体新宽度</param>
        /// <param name="nHeight">目标窗体新高度</param>
        /// <param name="BRePaint">是否刷新窗体</param>
        /// <returns></returns> 
        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "MoveWindow")]
        public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint);

        public static int MoveWindow(IntPtr hWnd, RECT rec, bool BRePaint = true)
        {
            return MoveWindow(hWnd, rec.Left, rec.Top, rec.Width, rec.Height, BRePaint);
        }

        public struct RECT
        {
            /// <summary>
            /// 左
            /// </summary>
            public int Left;
            /// <summary>
            /// 上
            /// </summary>
            public int Top;
            /// <summary>
            /// 右
            /// </summary>
            public int Right;
            /// <summary>
            /// 下
            /// </summary>
            public int Bottom;

            public RECT(int left, int top, int right, int bottom)
            {
                Left = left;
                Top = top;
                Right = right;
                Bottom = bottom;
            }

            /// <summary>
            /// 面积
            /// </summary>
            /// <returns></returns>
            public int Area
            {
                get
                {
                    return Width * Height;
                }
            }
            /// <summary>
            /// 宽度
            /// </summary>
            public int Width
            {
                get
                {
                    return Right - Left;
                }
            }
            /// <summary>
            /// 高度
            /// </summary>
            public int Height
            {
                get
                {
                    return Bottom - Top;
                }
            }

            /// <summary>
            /// 二维叉乘,计算 |p1 p2| X |p1 p|
            /// </summary>  
            /// <param name="a">直线点1</param>
            /// <param name="b">直线点2</param>  
            /// <param name="p">判断是否在内的点</param>
            /// <returns></returns>
            /// https://blog.csdn.net/pdcxs007/article/details/51436483
            private static double GetCross(POINT a, POINT b, POINT p)
            {
                var n = (b.X - a.X) * (p.Y - a.Y) - (p.X - a.X) * (b.Y - a.Y);
                return n;
            }

            /// <summary>
            /// 左下
            /// </summary>
            /// <returns></returns>
            public POINT Min
            {
                get { return new POINT(Left, Bottom); }
            }
            /// <summary>
            /// 左上
            /// </summary>
            /// <returns></returns>
            public POINT MinEx
            {
                get { return new POINT(Left, Top); }
            }
            /// <summary>
            /// 右上
            /// </summary>
            /// <returns></returns>
            public POINT Max
            {
                get { return new POINT(Right, Top); }
            }
            /// <summary>
            /// 右下
            /// </summary>
            /// <returns></returns>
            public POINT MaxEx
            {
                get { return new POINT(Right, Bottom); }
            }

            /// <summary>
            /// 点在矩形内
            /// </summary>
            /// <param name="p">判断的点</param> 
            public bool IsPointInMatrix(POINT p)
            {
                var p1 = Min;
                var p2 = MinEx;
                var p3 = Max;
                var p4 = MaxEx;

                return GetCross(p1, p2, p) * GetCross(p3, p4, p) >= 0 && GetCross(p2, p3, p) * GetCross(p4, p1, p) >= 0;
            }

            /// <summary>
            /// 有效的
            /// </summary>
            public bool IsValid
            {
                get
                {
                    return Left > 0 && Top > 0 && Right > 0 && Bottom > 0;
                }
            }

            public override string ToString()
            {
                var sb = new StringBuilder();
                sb.Append("(Left:");
                sb.Append(Left);
                sb.Append(",Top:");
                sb.Append(Top);
                sb.Append(",Right:");
                sb.Append(Right);
                sb.Append(",Bottom:");
                sb.Append(Bottom);
                sb.Append(")");
                return sb.ToString();
            }


            public Rectangle Rectangle
            {
                get
                {
                    return new Rectangle(Left, Top, Width, Height);
                }
            }
            public Size Size
            {
                get
                {
                    return new Size(Width, Height);
                }
            }

        }


        //https://www.cnblogs.com/zhuiyi/archive/2012/07/17/2595309.html
        /// <summary>
        /// 获取窗口客户区的大小,客户区为窗口中除标题栏,菜单栏之外的地方 
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lpRect"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetClientRect")]
        public static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);

        /// <summary>
        /// 将指定点的客户区坐标转换为屏幕区坐标
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="lpPoint"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "ClientToScreen")]
        public static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);

        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "ClientToScreen")]
        public static extern bool ClientToScreen(IntPtr hWnd, ref RECT rect); //这里可以用吗?

        /// <summary>
        /// 返回指定窗口边框的矩形尺寸,以屏幕左上角为0,0
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lpRect"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto, EntryPoint = "GetWindowRect")]
        public static extern int GetWindowRect(IntPtr hwnd, out RECT lpRect);

        //https://www.cnblogs.com/xtfnpgy/p/9285423.html
        /// <summary>
        /// 嵌入窗口,设置面板在某个面板的下面
        /// </summary>
        /// <param name="hWndChild">嵌入的面板来源</param>
        /// <param name="hWndNewParent">嵌入的面板目标</param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "SetParent", SetLastError = true)]
        public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        /// <summary>
        /// 非模态对话框切换焦点回cad
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "SetFocus")]
        public static extern int SetFocus(IntPtr hWnd);

        /// <summary>
        /// 当前焦点位于哪个控件上
        /// </summary>
        /// <returns></returns>
        [DllImport("user32.dll", EntryPoint = "GetFocus", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Winapi)]
        public static extern IntPtr GetFocus();

        /// <summary>
        /// 更新窗口
        /// </summary>
        /// <param name="hWnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool UpdateWindow(IntPtr hWnd);



        [DllImport("Kernel32.dll")]
        extern static int FormatMessage(int flag, ref IntPtr source, int msgid, int langid, ref string buf, int size, ref IntPtr args);

        /// <summary>
        /// 获取系统错误信息描述
        /// </summary>  
        /// <returns></returns>
        public static string GetSysErrMsg()
        {
            return GetSysErrMsg(Marshal.GetLastWin32Error());
        }

        /// <summary>
        /// 获取最后一次错误码
        /// </summary>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        public static extern uint GetLastError();

        /// <summary>
        /// 获取系统错误信息描述,这里可以直接输出信息,就不用原生错误码的那个了
        /// </summary>
        /// <param name="functionName">填入监测的函数名称</param>
        /// <param name="writeErrorMessage">输出提示吗?</param>
        /// <returns>0为正确执行</returns>
        public static uint GetLastError(string functionName, bool writeErrorMessage = true)
        {
            var errorNum = GetLastError();
            if (writeErrorMessage)
            {
                var error = GetSysErrMsg();
                if (!error.Contains("操作成功完成"))
                {
                    var sb = new StringBuilder();
                    sb.Append("函数:");
                    sb.Append(functionName);
                    sb.Append("->错误码:");
                    sb.Append(errorNum);
                    sb.Append("->错误信息:");
                    sb.Append(error);
                    Debug.WriteLine(sb);
                }
            }
            return errorNum;
        }

        /// <summary>
        /// 获取系统错误信息描述
        /// </summary>
        /// <param name="errCode">系统错误码</param>
        /// <returns></returns>
        public static string GetSysErrMsg(int errCode)
        {
            IntPtr tempptr = IntPtr.Zero;
            string msg = null;
            FormatMessage(0x1300, ref tempptr, errCode, 0, ref msg, 255, ref tempptr);
            return msg;
        }
    }
}
